//
// Created by cjh on 2021/5/28.
//

#ifndef RESOURCE_FUNCTION_H
#define RESOURCE_FUNCTION_H
#include <coroutine>
#include <memory>



/**
 * Rule: coroutine function is sync called, but the top level is async.
 *       in the global vision, it's a concurrency call.
 *
 * Never assign a coroutine function to variable, it will throw SIGSEGV
 * **/


class _async {
public:
    struct _promise {
        auto initial_suspend() noexcept { return std::suspend_always(); }
        void unhandled_exception() { throw; }
        auto final_suspend() noexcept { return std::suspend_never(); }
        _async* child = nullptr;
    };

    virtual ~_async() = default;
    virtual void resume()=0;
    virtual bool done()const=0;

    //! awaitable
    template<typename CoroHandle>
    void await_suspend(CoroHandle h) { h.promise().child = this; }
};


template<typename Return=void>
class async : public _async {
public:
    struct promise_type : public _async::_promise {
        async get_return_object() { return coroutine_handle::from_promise(*this); }
        void return_value(Return&& ret) { coro->value = std::move(ret); }
        async *coro = nullptr;
    };
    typedef std::coroutine_handle<async::promise_type> coroutine_handle;

    //! awaitable
    inline bool await_ready() const noexcept { h.resume(); return h.done(); }
    inline Return await_resume() { return std::move(value); }

    async(coroutine_handle handle):h(handle) { h.promise().coro = this; }
    async(const async &)=delete;
    async& operator=(const async &)=delete;

    //! virtual
    void resume() {
        if(h.promise().child) {
            h.promise().child->resume();
            if(h.promise().child->done()) {
                h.promise().child = nullptr;
                h.resume();
            }
        }else
            h.resume();
    }
    inline bool done()const { return h.done(); }

    Return value;
    coroutine_handle h;
};


template<>
class async<void> : public _async {
public:
    struct promise_type : public _async::_promise {
        async get_return_object() { return coroutine_handle::from_promise(*this); }
        inline void return_void() {}
        async *coro = nullptr;

    };
    typedef std::coroutine_handle<async::promise_type> coroutine_handle;

    //! awaitable
    inline bool await_ready() const noexcept { h.resume(); return h.done(); }
    inline void await_resume() {}

    async(coroutine_handle handle):h(handle) { h.promise().coro = this; }

    //! virtual
    void resume() {
        if(h.promise().child) {
            h.promise().child->resume();
            if(h.promise().child->done()) {
                h.promise().child = nullptr;
                h.resume();
            }
        }else
            h.resume();
    }
    inline bool done()const { return h.done(); }

    coroutine_handle h;
};


class async_function {
public:
    explicit async_function(_async *ptr=nullptr, bool _start=true);
    template<typename Return>
    explicit async_function(async<Return>&& func, bool _start=true):obj(std::forward<async<Return>>(func)) {
        if(_start) obj->resume();
    }
    ~async_function(){}

    inline void resume()const { obj->resume(); }
    inline bool done()const noexcept { return obj->done(); }
    inline void reset(_async*ptr=nullptr, bool _start=true) { obj.reset(ptr); if(ptr && _start) obj->resume(); }
    template<typename Return>
    async_function& reset(async<Return>&& func, bool _start=true) {
        obj = std::make_shared<async<Return>>(std::forward<async<Return>>(func));
        if(_start) obj->resume();
        return *this;
    }

    std::shared_ptr<_async> obj;
};


#endif //RESOURCE_FUNCTION_H
